package agendaSystem;

import java.util.*;

/**
 * A class for the creation of slotted entries based on the day, description,number of agendas and number of slots.
 * 
 * @invar	The description of each entry must be a valid description for an entry. 
 * 			| isValidDecription(getDescription())
 * @invar	The day of each entry must be a valid day for an entry.
 * 			| isValidDay(getDay())
 * @invar	The slot must be a valid slot for an entry.
 * 			| isValidSlot(getSlots()) 
 * 
 * @author	Sabrina Knapen, Axel Vranken
 * @version 1.0 
 * 
 */
public abstract class SlottedEntry extends Entry {
	
	/**
     * Initialize this new Slottedentry with given description, day, number of slots and number of agendas.
     * 
     * @param	description
	 * 			The description of the new entry.
	 * @param	day
	 * 			The day of the new entry.
	 * @param	agendas
	 * 			The agendas that shall contain the new entry.
	 * @param 	slots
	 * 			The  number of slots occupied by the entry.
	 * @pre		The description must be valid for the new entry. 
	 * 			| isValidDescription(description)
	 * @pre		The given day must be a valid day for the entry. 
	 * 			| isValidDay(day)
	 * @pre		The given slots must be valid.
	 * 			| for each slot in slots
	 * 			| 		isValidSlot(slot)
	 * @pre		The given agendas must exist.
	 * 			| for each ag in agendas
	 * 			|		isTerminated = false
	 * @post	The description of this new entry is equal to the given
	 * 			description. 
	 * 			| new.getDescription() == description
	 * @post	The day of this new entry is equal to the given day. 
	 * 			| new.getDay() == day
	 * @post	The agenda of this new entry is the same as the
	 *          given agenda.
	 *       	| new.getAgenda() == agenda
	 * @post	The given agendas have this new entry as one of the
	 *         	entries on the given day.
	 *         	| for each ag in agendas
	 *       	| 		ag.getEntriesAt(day).contains(this)
	 * @post    The number of entries for the given agendas on the given day is
	 *          incremented by 1.
	 *          | for each ag in agendas
	 *       	| 		ag.getAllEntries() == ag.getAllEntries() + 1
	 * @effect	The entry belongs to an array of agendas
	 * 			| for each ag in agendas
	 * 			| 		ag.addEntry(this)
	 * @effect	The agendas are added to the list of agendas for this entry.
	 * 			| for each ag in agenda
	 * 			| 		this.addAgenda(ag)
	 * @effect	The slots of this new entry will be added to the list of slots. 
	 * 			| for each slot in slots
	 * 			| 		addSlot(slots)
	 * @throws	AssertionError
	 * 			The number of slots must be effective and bigger than one.
	 * 			| (slots != null && slots.length != 0)
     */
	public SlottedEntry(Agenda[] agendas, String description, int day,
			int... slots) throws AssertionError {
		super(description, day);
		assert (slots != null);
		assert (slots.length != 0);
		Arrays.sort(slots);
		for (int slot : slots) {
			addSlot(slot);
		}
		for(Agenda ag:agendas){
			ag.addEntry(this);
		}
	}
	
	/**
	 * Checks whether the given slot is a valid slot for an entry.
	 * 
	 * @param	slot
	 * 			The slot that needs to be checked.
	 * @return	True if and only if the given slot is not smaller than 1 and not bigger than the maximum number of slots.
	 * 			|result != (slot < 1) && (slot > getMaxSlot())
	 */
	public boolean isValidSlot(int slot) {
		return (!(slot < 1 && slot > getMaxSlot()));
	}

	/**
	 * Returns the maximum amount of slots on one day.
	 */
	public static int getMaxSlot() {
		return MAXSLOTS;
	}
	
	/**
	 * Variable setting the maximum of slots.
	 */
	private final static int MAXSLOTS = 24;

	/**
	 * 
	 * @return	Returns the slots attached to the entry. The slots are the hours
	 * 			scheduled for the entry.
	 */
	@Override 
	public boolean[] getSlots() {
		return this.slots;
	}
	/**
	 * A method for adding a given slot to an entry.
	 * 
	 * @param	slot
	 * 			The slot that needs to be added to the array of booleans (slots).
	 * @post	The slot will be added to the array of booleans slots. The given
	 * 			slot will be set true on a given index. The index in the array of slots is the number slot-1.
	 * 			|this.getSlots()[slot - 1] = true	
	 * @throws	IllegalArgumentException
	 * 			The given slot must be a valid slot for the entry.
	 * 			| isValidSlot(slot)
	 */
	public void addSlot(int slot) throws IllegalArgumentException {
		if (!isValidSlot(slot))
			throw new IllegalArgumentException(
					"This is not a valid slot to add to the entry.");
		else {
			int i = slot-1;
			this.getSlots()[i]= true;
		}
	}

	/**
	 * A method for removing a given slot from an entry.
	 * 
	 * @param	slot
	 *			The slot that needs to be removed from the array of booleans
	 * 			(slots).
	 * @post	The slot will be removed from the array of booleans (slots). The
	 *			given slot will be set false on a given index.The index in the array of slots is the number slot-1. 
	 *			|this.getSlots()[slot - 1] = false	 
	 * @throws	IllegalArgumentException
	 * 			The given slot must be a valid slot for the entry.
	 * 			| isValidSlot(slot)
	 */
	public void removeSlot(int slot)
			throws IllegalArgumentException {
		if (this.getSlots()[slot-1]!= true)
			throw new IllegalArgumentException(
					"This slot cannot be removed from the given entry.");
		else {
			int i = slot-1;
			this.getSlots()[i] = false;
		}
	}

	/**
	 * A method for getting the first slot of a given entry.
	 * 
	 * @param	entry
	 * 			The given entry where you want to get the first slot from.
	 * @return	Checks for each slot in the array slots which slot is set true first, starting 
	 * 			at the beginning of the array and returns the first slot.
	 * 			| for each slot in slots
	 * 			|		startindex = 0
	 * 			|			if (slots[index] == true)
	 * 			|					then return slot		
	 * 
	 */
	public int getFirstSlot() {
		int firstSlot = 0;
		for (int i = 0; i < this.getSlots().length; i++) { 
			if (this.getSlots()[i] == true) {
				firstSlot = i+1;
				break;
			}
		}
		return firstSlot;
	}

	/**
	 * A method to get the last entry of a given entry.
	 * 
	 * @param	entry
	 * 			The given entry where you want to get the last slot from.
	 * @return	Checks in the array slots which slot is set true first, starting 
	 * 			at the end of the array and returns the last slot.
	 * 			| for each slot in slots
	 * 			|		startindex = slots.length-1
	 * 			|			if (slots[index] == true)
	 * 			|					then return slot			
	 */
	public int getLastSlot() {
		int lastSlot = 0;
		for (int i = this.getSlots().length-1; i >= 0; i--) {
			if (this.getSlots()[i] == true) 
			{
				lastSlot = i+1;
				break;
			} 
		}
		return lastSlot;
	}

	/**
	 * A method to get the total number of slots from a given entry.
	 * 
	 * @param	entry
	 * 			The given entry where you want to get the number of slots from.
	 * @return	Checks in the array of slots from an entry which slots are set true and 
	 *			sums the slots to give the total number of slots.
	 *			| for each slot in slots
	 *			|		if(slot == true)
	 *			|			then numberOfSlots + 1
	 *			|				return numberOfSlots
	 */
	public int getNbSlots() {
		int numberOfSlots = 0;
		for (int i = 0; i < this.getSlots().length; i++) {
			if (this.getSlots()[i] == true) 
				numberOfSlots = numberOfSlots + 1;
		}
		return numberOfSlots;
	}

	/**
	 * A method to check whether a slot of the given entry overlaps with a slot
	 * from another entry.
	 * 
	 * @param	other
	 * 			The other entry you want to compare with.
	 * @return	Returns true if and only if the given entry has a same slot set on true as the other entry.
	 *			| for each slot in slots=this.getSlots() of the entry
	 *			|	if ((this.getSlots() == true) && (other.getSlots() == true)) 
	 *			|		then return true
	 * @throws	IllegalArgumentException
	 *			The other entry must be effective.
	 *			| other != null	
	 */
	@Override
	public boolean overlapsWith(Entry other) throws IllegalArgumentException{
		try {
			for (int i = 0; i < other.getSlots().length; i++) {
				if ((this.getSlots()[i] == true) && (other.getSlots()[i] == true)) 
					return true;
			}
		}
		catch (NullPointerException exc) {
			assert (other == null);
			throw new IllegalArgumentException();
		}
		return false;
	}

	/**
	 * A method to check whether a slot from the given entry starts before a
	 * slot from another entry.
	 *
	 * @param	other
	 * 			The other entry to compare with.
	 * @return	Returns true if and only if the given entry starts before the other entry.
	 * 			| if (this.getFirstSlot() < other.getFirstSlot())
	 *			| 	then return true
	 * @throws	IllegalArgumentException
	 *			The other entry must be effective.
	 *			| other != null	
	 */
	public boolean startsBefore(Entry other) throws IllegalArgumentException {
		try {
			if (this.getFirstSlot() < other.getFirstSlot())
				return true;
			else {
				return false;
			}
		}
		catch (NullPointerException exc) {
			assert (other == null);
			throw new IllegalArgumentException();
		}
	}
	
	/**
	 * Method for moving an entry a given number of slots
	 * 
	 * @param shift
	 * 		  The number of slots to move a given entry.
	 * @post  For each slot in the list of slots of the entry we add the number shift to it.
	 * 		  We remove the slot from the entry, we create a new newslot (oldslot + shift) 
	 * 		  and we add it back to the entry.
	 * 		  | for each slot in slots
	 * 		  |		this.removeSlot(slot)
	 * 		  |		newslot = oldslot + shift
	 * 		  |		this.addSlot(newslot)
	 * @post  The firstSlot of the entry has been moved to a new first slot equal to the number shift added to the old slot.
	 * 		  |	new.getFirstSlot() = getFirstSlot() + shift
	 * @post  The lastSlot of the entry has been moved to a new last slot equal to the number shift added to the old slot.
	 * 		  |	new.getLastSlot() = getLastSlot() + shift
	 * @throws IllegalArgumentException	
	 * 		  The newSlot must be a valid number of slots.
	 * 		  |	isValidSlot(newSlot)	
	 * 
	 */
	public void moveNbSlots(int shift) throws IllegalArgumentException {
		boolean[] slotsOfThisEntry = this.getSlots();
		List<Integer> newSlots = new ArrayList<Integer>();
		if (shift >= 0) {
			for (int i = slotsOfThisEntry.length - 1; i >= 0; i--) {
				if (slotsOfThisEntry[i] == true) {
					this.removeSlot(i + 1);
					newSlots.add(i + 1 + shift);
				}
			}
		}
		else {
			for (int i = 0; i <= slotsOfThisEntry.length - 1; i++) {
				if (slotsOfThisEntry[i] == true) {
					this.removeSlot(i + 1);
					newSlots.add(i + 1 + shift);
				}
			}
		}
		for (int newSlot : newSlots)
			this.addSlot(newSlot);
	}

	/**
	 * Array registering the availability of a slot. 
	 */
	private boolean[] slots = new boolean[24];

}
